package com.virjar.vscrawler.core.grab.multiaction;


import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.virjar.vscrawler.core.util.ClassScanner;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Modifier;
import java.util.ArrayList;

/**
 * 在注解模式下，创建多action wrapper，基于注解扫描方案
 *
 * @author dengweijia
 * @since 1.0.4
 */
@Slf4j
public class MultiActionWrapperFactory {


    /**
     * hermes agent 1.8 以前的版本，无法自动感知注解wrapper，需要自己手动继承MultiActionWrapper，然后调用此方法，进行手动注册。
     * <pre>
     *     public class XXAppMulitiActionWrapper extends MultiActionWrapper {
     *          public XXAppMulitiActionWrapper() {
     *               //挂载XXAppMulitiActionWrapper.class 同级别路径和子路径的actionHandler
     *              MultiActionWrapperFactory.registerActionHandlers(this, XXAppMulitiActionWrapper.class.getPackage().getName());
     *          }
     *      }
     * </pre>
     *
     * @param multiActionWrapper multiActionWrapper基类实现
     * @param scanBasePackage    注册基于扫描机制，需要提供一个base路径
     */
    public static void registerActionHandlers(MultiActionProcessor multiActionWrapper, String scanBasePackage) {
        ArrayList<ActionRequestHandler> actionRequestHandlers = scanActionWrappers(scanBasePackage);
        if (actionRequestHandlers.size() == 0) {
            throw new IllegalStateException("can not find action handler implement for base package: " + scanBasePackage);
        }
        for (ActionRequestHandler actionRequestHandler : actionRequestHandlers) {
            multiActionWrapper.registryHandler(resolveAction(actionRequestHandler), actionRequestHandler);
        }
    }


    public static ArrayList<ActionRequestHandler> scanActionWrappers(String packageName) {
        ClassScanner.AnnotationClassVisitor annotationClassVisitor = new ClassScanner.AnnotationClassVisitor(GrabAction.class);
        ClassScanner.scan(annotationClassVisitor, Sets.newHashSet(packageName));

        return Lists.newArrayList(Iterables.filter(Iterables.transform(Iterables.filter(annotationClassVisitor.getClassSet(), new Predicate<Class>() {
            @Override
            public boolean apply(Class input) {
                if (input == null) {
                    return false;
                }
                if (Modifier.isAbstract(input.getModifiers()) || input.isInterface()) {
                    return false;
                }
                GrabAction wrapperAction = (GrabAction) input.getAnnotation(GrabAction.class);
                return !wrapperAction.value().trim().isEmpty() && ActionRequestHandler.class.isAssignableFrom(input);
            }
        }), new Function<Class, ActionRequestHandler>() {
            @Override
            public ActionRequestHandler apply(Class input) {
                if (input == null) {
                    return null;
                }
                try {
                    return (ActionRequestHandler) input.newInstance();
                } catch (Exception e) {
                    log.error("weijia", "failed to load create plugin", e);
                    return null;
                }
            }
        }), new Predicate<ActionRequestHandler>() {
            @Override
            public boolean apply(ActionRequestHandler input) {
                return input != null;
            }
        }));
    }


    public static String resolveAction(ActionRequestHandler actionRequestHandler) {
        return actionRequestHandler.getClass().getAnnotation(GrabAction.class).value();
    }


    public static MultiActionProcessor createWrapperByAction(String packageName) {
        ArrayList<ActionRequestHandler> actionRequestHandlers = scanActionWrappers(packageName);
        if (actionRequestHandlers.size() == 0) {
            return null;
        }
        MultiActionProcessor multiActionWrapper = new MultiActionProcessor();
        for (ActionRequestHandler actionRequestHandler : actionRequestHandlers) {
            multiActionWrapper.registryHandler(resolveAction(actionRequestHandler), actionRequestHandler);
        }
        return multiActionWrapper;
    }
}
